﻿/***************************************************
//  Copyright (c) Premium Tax Free 2011
***************************************************/

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;

namespace PremierTaxFree.PTFLib.Extensions
{
    public enum ColorFilterTypes
    {
        Red,
        Blue,
        Green,
    }

    public static class ImageEx
    {
        public static byte[] ToArray(this Image bmp)
        {
            using (MemoryStream mem = new MemoryStream())
            {
                bmp.Save(mem, bmp.RawFormat);
                return mem.ToArray();
            }
        }

        public static Cursor CreateCursor(this Bitmap bmp, int xHotSpot, int yHotSpot)
        {
            IconInfo tmp = new IconInfo();
            GetIconInfo(bmp.GetHicon(), ref tmp);
            tmp.xHotspot = xHotSpot;
            tmp.yHotspot = yHotSpot;
            tmp.fIcon = false;
            return new Cursor(CreateIconIndirect(ref tmp));
        }

        public static Bitmap Resize(this Bitmap bmp, int nWidth, int nHeight)
        {
            if (nWidth < 0 || nHeight < 0)
                return bmp;

            using (bmp)
            {
                Bitmap copy = new Bitmap(nWidth, nHeight);
                using (Graphics g = Graphics.FromImage(copy))
                    g.DrawImage(bmp, 0, 0, nWidth, nHeight);
                return copy;
            }
        }

        public static Bitmap Rotate(this Bitmap bmp, float angle)
        {
            if (float.IsNaN(angle))
                return null;

            using (bmp)
            {
                //create a new empty bitmap to hold rotated image
                Bitmap copy = new Bitmap(bmp.Width, bmp.Height);
                //make a graphics object from the empty bitmap
                using (Graphics g = Graphics.FromImage(copy))
                {
                    //move rotation point to center of image
                    g.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);
                    //rotate
                    g.RotateTransform(angle);
                    //move image back
                    g.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);
                    //draw passed in image onto graphics object
                    g.DrawImage(bmp, new Point(0, 0));
                    return copy;
                }
            }
        }

        /// <summary>
        /// ChangePixelFormat
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="newFormat"></param>
        /// <returns></returns>
        /// <see cref="http://stackoverflow.com/questions/483014/generate-image-file-with-low-bit-depths"/>
        public static Bitmap ChangePixelFormat(this Bitmap bmp, PixelFormat newFormat)
        {
            Debug.Assert(bmp != null);
            using (bmp)
            {
                Bitmap copy = new Bitmap(bmp.Width, bmp.Height, newFormat);
                using (Graphics g = Graphics.FromImage(copy))
                {
                    g.DrawImage(bmp, 0, 0);
                }
                return copy;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="colorFilterType"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap SetColorFilter(this Bitmap bmp, ColorFilterTypes colorFilterType)
        {
            Debug.Assert(bmp != null);
            using (bmp)
            {
                Bitmap clone = (Bitmap)bmp.Clone();

                for (int i = 0; i < clone.Width; i++)
                {
                    for (int j = 0; j < clone.Height; j++)
                    {
                        Color c = clone.GetPixel(i, j);

                        int nPixelR = 0;
                        int nPixelG = 0;
                        int nPixelB = 0;

                        if (colorFilterType == ColorFilterTypes.Red)
                        {
                            nPixelR = c.R;
                            nPixelG = c.G - 255;
                            nPixelB = c.B - 255;
                        }
                        else if (colorFilterType == ColorFilterTypes.Green)
                        {
                            nPixelR = c.R - 255;
                            nPixelG = c.G;
                            nPixelB = c.B - 255;
                        }
                        else if (colorFilterType == ColorFilterTypes.Blue)
                        {
                            nPixelR = c.R - 255;
                            nPixelG = c.G - 255;
                            nPixelB = c.B;
                        }

                        nPixelR = Math.Max(nPixelR, 0);
                        nPixelR = Math.Min(255, nPixelR);

                        nPixelG = Math.Max(nPixelG, 0);
                        nPixelG = Math.Min(255, nPixelG);

                        nPixelB = Math.Max(nPixelB, 0);
                        nPixelB = Math.Min(255, nPixelB);

                        clone.SetPixel(i, j, Color.FromArgb((byte)nPixelR, (byte)nPixelG, (byte)nPixelB));
                    }
                }
                return clone;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap SetGamma(this Bitmap bmp, double red, double green, double blue)
        {
            Debug.Assert(bmp != null);
            using (bmp)
            {
                Bitmap clone = (Bitmap)bmp.Clone();

                byte[] redGamma = CreateGammaArray(red);
                byte[] greenGamma = CreateGammaArray(green);
                byte[] blueGamma = CreateGammaArray(blue);

                for (int i = 0; i < clone.Width; i++)
                {
                    for (int j = 0; j < clone.Height; j++)
                    {
                        Color c = clone.GetPixel(i, j);
                        clone.SetPixel(i, j, Color.FromArgb(redGamma[c.R], greenGamma[c.G], blueGamma[c.B]));
                    }
                }
                return clone;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap SetBrightness(this Bitmap bmp, int brightness)
        {
            Debug.Assert(bmp != null);
            using (bmp)
            {
                Bitmap clone = (Bitmap)bmp.Clone();
                if (brightness < -255) brightness = -255;
                if (brightness > 255) brightness = 255;

                for (int i = 0; i < clone.Width; i++)
                {
                    for (int j = 0; j < clone.Height; j++)
                    {
                        Color c = clone.GetPixel(i, j);

                        int cR = c.R + brightness;
                        int cG = c.G + brightness;
                        int cB = c.B + brightness;

                        if (cR < 0) cR = 1;
                        if (cR > 255) cR = 255;

                        if (cG < 0) cG = 1;
                        if (cG > 255) cG = 255;

                        if (cB < 0) cB = 1;
                        if (cB > 255) cB = 255;

                        clone.SetPixel(i, j, Color.FromArgb((byte)cR, (byte)cG, (byte)cB));
                    }
                }
                return clone;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap SetContrast(this Bitmap bmp, double contrast)
        {
            Debug.Assert(bmp != null);

            using (bmp)
            {
                Bitmap clone = (Bitmap)bmp.Clone();
                if (contrast < -100) contrast = -100;
                if (contrast > 100) contrast = 100;
                contrast = (100.0 + contrast) / 100.0;
                contrast *= contrast;

                for (int i = 0; i < clone.Width; i++)
                {
                    for (int j = 0; j < clone.Height; j++)
                    {
                        Color c = clone.GetPixel(i, j);
                        double pR = c.R / 255.0;
                        pR -= 0.5;
                        pR *= contrast;
                        pR += 0.5;
                        pR *= 255;
                        if (pR < 0) pR = 0;
                        if (pR > 255) pR = 255;

                        double pG = c.G / 255.0;
                        pG -= 0.5;
                        pG *= contrast;
                        pG += 0.5;
                        pG *= 255;
                        if (pG < 0) pG = 0;
                        if (pG > 255) pG = 255;

                        double pB = c.B / 255.0;
                        pB -= 0.5;
                        pB *= contrast;
                        pB += 0.5;
                        pB *= 255;
                        if (pB < 0) pB = 0;
                        if (pB > 255) pB = 255;

                        clone.SetPixel(i, j, Color.FromArgb((byte)pR, (byte)pG, (byte)pB));
                    }
                }
                return clone;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap SetGrayscale(this Bitmap bmp)
        {
            Debug.Assert(bmp != null);
            using (bmp)
            {
                Bitmap clone = (Bitmap)bmp.Clone();

                for (int i = 0; i < clone.Width; i++)
                {
                    for (int j = 0; j < clone.Height; j++)
                    {
                        Color c = clone.GetPixel(i, j);
                        byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);
                        clone.SetPixel(i, j, Color.FromArgb(gray, gray, gray));
                    }
                }
                return clone;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap SetInvert(this Bitmap bmp)
        {
            Debug.Assert(bmp != null);
            using (bmp)
            {
                Bitmap clone = (Bitmap)bmp.Clone();

                for (int i = 0; i < clone.Width; i++)
                {
                    for (int j = 0; j < clone.Height; j++)
                    {
                        Color c = clone.GetPixel(i, j);
                        clone.SetPixel(i, j, Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B));
                    }
                }
                return clone;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap SetSize(this Bitmap bmp, int newWidth, int newHeight)
        {
            Debug.Assert(bmp != null);
            Debug.Assert(newWidth != 0 && newHeight != 0);

            using (bmp)
            {
                Bitmap copy = new Bitmap(newWidth, newHeight, bmp.PixelFormat);

                double nWidthFactor = (double)bmp.Width / (double)newWidth;
                double nHeightFactor = (double)bmp.Height / (double)newHeight;

                for (int x = 0; x < copy.Width; ++x)
                {
                    for (int y = 0; y < copy.Height; ++y)
                    {

                        int fr_x = (int)Math.Floor(x * nWidthFactor);
                        int fr_y = (int)Math.Floor(y * nHeightFactor);
                        int cx = fr_x + 1;
                        if (cx >= bmp.Width) cx = fr_x;
                        int cy = fr_y + 1;
                        if (cy >= bmp.Height) cy = fr_y;
                        double fx = x * nWidthFactor - fr_x;
                        double fy = y * nHeightFactor - fr_y;
                        double nx = 1.0 - fx;
                        double ny = 1.0 - fy;

                        Color color1 = bmp.GetPixel(fr_x, fr_y);
                        Color color2 = bmp.GetPixel(cx, fr_y);
                        Color color3 = bmp.GetPixel(fr_x, cy);
                        Color color4 = bmp.GetPixel(cx, cy);

                        // Blue
                        byte bp1 = (byte)(nx * color1.B + fx * color2.B);

                        byte bp2 = (byte)(nx * color3.B + fx * color4.B);

                        byte nBlue = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        // Green
                        bp1 = (byte)(nx * color1.G + fx * color2.G);

                        bp2 = (byte)(nx * color3.G + fx * color4.G);

                        byte nGreen = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        // Red
                        bp1 = (byte)(nx * color1.R + fx * color2.R);

                        bp2 = (byte)(nx * color3.R + fx * color4.R);

                        byte nRed = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        copy.SetPixel(x, y, Color.FromArgb(255, nRed, nGreen, nBlue));
                    }
                }
                return copy;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap RotateFlip(this Bitmap bmp, RotateFlipType rotateFlipType)
        {
            Debug.Assert(bmp != null);
            using (bmp)
            {
                Bitmap clone = (Bitmap)bmp.Clone();
                clone.RotateFlip(rotateFlipType);
                return clone;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap DrawOutCropArea(this Bitmap bmp, int xPosition, int yPosition, int width, int height)
        {
            using (bmp)
            {
                Bitmap clone = (Bitmap)bmp.Clone();

                using (Graphics gr = Graphics.FromImage(clone))
                {
                    using (var pen = new Pen(Color.FromArgb(150, Color.White)))
                    {
                        Rectangle rect1 = new Rectangle(0, 0, bmp.Width, yPosition);
                        Rectangle rect2 = new Rectangle(0, yPosition, xPosition, height);
                        Rectangle rect3 = new Rectangle(0, (yPosition + height), bmp.Width, bmp.Height);
                        Rectangle rect4 = new Rectangle((xPosition + width), yPosition, (bmp.Width - xPosition - width), height);
                        gr.FillRectangle(pen.Brush, rect1);
                        gr.FillRectangle(pen.Brush, rect2);
                        gr.FillRectangle(pen.Brush, rect3);
                        gr.FillRectangle(pen.Brush, rect4);
                    }
                    return clone;
                }
            }
        }

        public static Bitmap Crop(this Bitmap bmp, Rectangle rect)
        {
            return bmp.Crop(rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="rect"></param>
        /// <returns></returns>
        /// <see cref="http://www.codeproject.com/KB/GDI-plus/ImageProcessing2.aspx"/>
        public static Bitmap Crop(this Bitmap bmp, int xPosition, int yPosition, int width, int height)
        {
            Debug.Assert(bmp != null);

            using(bmp)
            using (Bitmap clone = (Bitmap)bmp.Clone())
            {
                if (xPosition + width > bmp.Width)
                    width = bmp.Width - xPosition;

                if (yPosition + height > bmp.Height)
                    height = bmp.Height - yPosition;

                Rectangle rect = new Rectangle(xPosition, yPosition, width, height);

                return (Bitmap)clone.Clone(rect, clone.PixelFormat);
            }
        }

        private static byte[] CreateGammaArray(double color)
        {
            byte[] gammaArray = new byte[256];
            for (int i = 0; i < 256; ++i)
                gammaArray[i] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(i / 255.0, 1.0 / color)) + 0.5));
            return gammaArray;
        }

        public struct IconInfo
        {
            public bool fIcon;
            public int xHotspot;
            public int yHotspot;
            public IntPtr hbmMask;
            public IntPtr hbmColor;
        }

        [DllImport("user32.dll")]
        private static extern IntPtr CreateIconIndirect(ref IconInfo icon);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
    }
}
